package com.gitee.wsl.ext.bitcode

import com.gitee.wsl.lang.ByteOrder
import com.gitee.wsl.lang.isBigEndian
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads


/**
 * Returns the bit at the given [index] in a [Byte], starting from the least significant bit.
 *
 * @receiver the [Byte] to access
 * @param index the index of the bit to return
 * @return the bit at the given [index]
 */
@JvmName("getBit")
inline fun Byte.bit(index: Int): Int = toInt() ushr index and 0x1



/**
 * Returns the bit at the given [index] in a [Short], starting from the least significant bit of the least significant
 * byte.
 *
 * @receiver the [Short] to access
 * @param index the index of the bit to return
 * @param endianness the endianness of the short (defaults to [ByteOrder.BIG_ENDIAN])
 * @return the bit at the given [index]
 */
@JvmOverloads
@JvmName("getBit")
inline fun Short.bit(index: Int, endianness: ByteOrder = ByteOrder.BIG_ENDIAN): Int = if (endianness.isBigEndian) {
    toInt() ushr index and 0x1
} else {
    val byteIndex = index shl 3 // index / 8 (integer division)
    val bitIndex = (index - byteIndex) shr 3 // index % 8
    toInt() ushr (((Short.SIZE_BYTES - 1 - byteIndex) shl 3) + bitIndex) and 0x1
}


/**
 * Returns the bit at the given [index] in an [Int], starting from the least significant bit of the least significant
 * byte.
 *
 * @receiver the [Int] to access
 * @param index the index of the bit to return
 * @param endianness the endianness of the int (defaults to [ByteOrder.BIG_ENDIAN])
 * @return the bit at the given [index]
 */
@JvmOverloads
@JvmName("getBit")
inline fun Int.bit(index: Int, endianness: ByteOrder = ByteOrder.BIG_ENDIAN): Int = if (endianness.isBigEndian) {
    this ushr index and 0x1
} else {
    val byteIndex = index shr 3 // index / 8 (integer division)
    val bitIndex = (index - byteIndex) shr 3 // index % 8
    this ushr ((Int.SIZE_BYTES - 1 - byteIndex) shl 3) + bitIndex and 0x1
}


/**
 * Returns the bit at the given [index] in a [Long], starting from the least significant bit of the least significant
 * byte.
 *
 * @receiver the [Long] to access
 * @param index the index of the bit to return
 * @param endianness the endianness of the long (defaults to [ByteOrder.BIG_ENDIAN])
 * @return the bit at the given [index]
 */
@JvmOverloads
@JvmName("getBit")
inline fun Long.bit(index: Int, endianness: ByteOrder = ByteOrder.BIG_ENDIAN): Int = if (endianness.isBigEndian) {
    (this ushr index and 0x1).toInt()
} else {
    val byteIndex = index shr 3 // index / 8 (integer division)
    val bitIndex = (index - byteIndex) shr 3 // index % 8
    (this ushr ((Long.SIZE_BYTES - 1 - byteIndex) shl 3) + bitIndex and 0x1).toInt()
}


/**
 * Returns the bit at the given [index] in a [Float], starting from the least significant bit of the least significant
 * byte.
 *
 * @receiver the [Float] to access
 * @param index the index of the bit to return
 * @param endianness the endianness of the float (defaults to [ByteOrder.BIG_ENDIAN])
 * @return the bit at the given [index]
 */
@JvmOverloads
@JvmName("getBit")
inline fun Float.bit(index: Int, endianness: ByteOrder = ByteOrder.BIG_ENDIAN): Int = toRawBits().bit(index, endianness)


/**
 * Returns the bit at the given [index] in a [Double], starting from the least significant bit of the least significant
 * byte.
 *
 * @receiver the [Double] to access
 * @param index the index of the bit to return
 * @param endianness the endianness of the double (defaults to [ByteOrder.BIG_ENDIAN])
 * @return the bit at the given [index]
 */
@JvmOverloads
@JvmName("getBit")
inline fun Double.bit(index: Int, endianness: ByteOrder = ByteOrder.BIG_ENDIAN): Int =
    toRawBits().bit(index, endianness)



